/*
* Copyright (c) 2018-2022 Allwinner Technology Co. Ltd. All rights reserved.
* Author: BT Team
* Date: 2022.03.12
* Description: gatt-client test demo.
*/

#include "bt_hal.h"

int gattc_cn_handle = 0;
static char gattc_select_addr[18];
static u8 now_connect_slave[6] = {0};
static u8 ble_scan_status = 0;
static int discovery_flag = 0;
#define CMD_NOTIFY_INDICATE_ID_MAX 20
static int notify_indicate_id_save[CMD_NOTIFY_INDICATE_ID_MAX + 1];
static ms_hal_ble_scan_callback_t scan_callback = NULL;
static btmg_le_scan_param_t scan_param;
static ms_hal_ble_scan_t scan_report_data[2] = {0};

static void bt_test_gattc_scan_report_cb(btmg_le_scan_report_t *data) {
    if (data->adv_type == 0) {
        bzero(scan_report_data, sizeof(scan_report_data));
    }
    if (scan_param.scan_type == 0)
        scan_report_data[0].type = MS_HAL_BLE_REPORT_TYPE_SCAN_IND;
    else
        scan_report_data[0].type = MS_HAL_BLE_REPORT_TYPE_SCAN_RSP;
    memcpy(scan_report_data[0].peer_addr.addr, data->peer_addr, MS_HAL_BLE_ADDR_LEN);
    scan_report_data[0].peer_addr.addr_type = data->adv_type;
    scan_report_data[0].rssi = data->rssi;
    scan_report_data[0].data = data->report.data;
    scan_report_data[0].len = data->report.data_len;
    if (scan_param.scan_type == 0){
        if (scan_callback)
            scan_callback(scan_report_data);
    }
    if (data->adv_type == 1) {
        scan_report_data[1].type = MS_HAL_BLE_REPORT_TYPE_SCAN_RSP;
        memcpy(scan_report_data[1].peer_addr.addr, data->peer_addr, MS_HAL_BLE_ADDR_LEN);
        scan_report_data[1].peer_addr.addr_type = data->adv_type;
        scan_report_data[1].rssi = data->rssi;
        scan_report_data[1].data = data->report.data;
        scan_report_data[1].len = data->report.data_len;
        if (scan_callback)
            scan_callback(scan_report_data);
    }

    // int j = 0;
    // int type;
    // char name[31] = {0};
    // char addrstr[64] = {0};

    // for (;;) {
    //     type = data->report.data[j + 1];
    //     // complete local name.
    //     if (type == 0x09) {
    //         memcpy(name, &data->report.data[j + 2], data->report.data[j] - 1);
    //         name[data->report.data[j] - 1] = '\0';
    //     }
    //     j = j + data->report.data[j] + 1;
    //     if (j >= data->report.data_len)
    //         break;
    // }

    // snprintf(addrstr, 64, "%02X:%02X:%02X:%02X:%02X:%02X (%s)", data->peer_addr[5], data->peer_addr[4], data->peer_addr[3],
    //          data->peer_addr[2], data->peer_addr[1], data->peer_addr[0],
    //          (data->addr_type == BTMG_LE_PUBLIC_ADDRESS) ? ("public")
    //                                                      : ((data->addr_type == BTMG_LE_RANDOM_ADDRESS) ? ("random") : ("error")));

    // printf("[devices]: %s, adv type:%d, rssi:%d %s\n", addrstr, data->adv_type, data->rssi, name);
}

static void bt_test_gattc_notify_indicate_cb(gattc_notify_indicate_cb_para_t *data) {
    //这个接口目前是有异议，暂不实现
    ms_hal_ble_stack_msg_t msg;
    bzero(&msg, sizeof(msg));
    msg.event_type = MS_HAL_BLE_STACK_EVENT_NOTIFY_REQ;
    // msg.event_type = MS_HAL_BLE_STACK_EVENT_INDICATE_REQ;
    // msg.param.conn_hdl = data->conn_id;  //要在注册通知的时候做好映射，根据value_handle来做判断
    msg.param.disc_char_msg.att_hdl = data->value_handle;
    msg.param.disc_char_msg.uuid_len = data->length;
    msg.param.disc_char_msg.uuid = data->value;
    if (ms_hal_ble_stack_callback)
        ms_hal_ble_stack_callback(msg);
}

static void bt_test_gattc_write_long_cb(gattc_write_long_cb_para_t *data) {
    if (data->success) {
        printf("Write successful\n");
    } else if (data->reliable_error) {
        printf("Reliable write not verified\n");
    } else {
        printf("\nWrite failed: %s (0x%02x)\n", bt_manager_gatt_client_ecode_to_string(data->att_ecode), data->att_ecode);
    }
}

static void bt_test_gattc_write_cb(gattc_write_cb_para_t *data) {
    if (data->success) {
        printf("\nWrite successful\n");
    } else {
        printf("\nWrite failed: %s (0x%02x)\n", bt_manager_gatt_client_ecode_to_string(data->att_ecode), data->att_ecode);
    }
}

static void bt_test_gattc_read_cb(gattc_read_cb_para_t *data) {
    int i;

    if (!data->success) {
        printf("\nRead request failed: %s (0x%02x)\n", bt_manager_gatt_client_ecode_to_string(data->att_ecode), data->att_ecode);
        return;
    }

    printf("\nRead value");

    if (data->length == 0) {
        printf(": 0 bytes\n");
        return;
    }

    printf(" (%u bytes): ", data->length);

    for (i = 0; i < data->length; i++)
        printf("%02x ", data->value[i]);

    printf("\n");
}

static void bt_test_gattc_conn_cb(gattc_conn_cb_para_t *data) {
    if (!data->success) {
        BTMG_INFO("gattc connect failed, error code: 0x%02x", data->att_ecode);
        return;
    }
    ms_hal_ble_stack_msg_t msg;
    bzero(&msg, sizeof(msg));
    msg.event_type = MS_HAL_BLE_STACK_EVENT_CONNECTION_REPORT;
    msg.param.conn_hdl = data->conn_id;
    memcpy(msg.param.connection_msg.addr, now_connect_slave, MS_HAL_BLE_ADDR_LEN);
    if (ms_hal_ble_stack_callback)
        ms_hal_ble_stack_callback(msg);
    ble_gatt_data_t device = {0};
    memcpy(device.addr, now_connect_slave, MS_HAL_BLE_ADDR_LEN);
    device.conn_id = data->conn_id;
    device.is_slave = 0;
    add_device_to_list(device);
    gattc_cn_handle = data->conn_id;
    BTMG_INFO("gattc connect completed, conn_id = 0x%x", gattc_cn_handle);
    ;
    bzero(now_connect_slave, sizeof(now_connect_slave));
}

static void bt_test_gattc_disconn_cb(gattc_disconn_cb_para_t *data) {
    BTMG_INFO("Device disconnected");
    ms_hal_ble_stack_msg_t msg;
    bzero(&msg, sizeof(msg));
    msg.event_type = MS_HAL_BLE_STACK_EVENT_DISCONNECTED;
    msg.param.conn_hdl = data->conn_handle;
    msg.param.disconnect_msg.reason = data->reason;
    if (ms_hal_ble_stack_callback)
        ms_hal_ble_stack_callback(msg);
    del_device_to_list(data->conn_handle);
    switch (data->reason) {
    case LOCAL_HOST_TERMINATED:
        BTMG_INFO("reason: LOCAL_HOST_TERMINATED");
        break;
    case CONNECTION_TIMEOUT:
        BTMG_INFO("reason: CONNECTION_TIMEOUT");
        break;
    case REMOTE_USER_TERMINATED:
        BTMG_INFO("reason: REMOTE_USER_TERMINATED");
        break;
    default:
        BTMG_INFO("reason: UNKNOWN_OTHER_ERROR");
    }
}

static void bt_test_gattc_service_changed_cb(gattc_service_changed_cb_para_t *data) {
    BTMG_INFO("\nService Changed handled - start: 0x%04x end: 0x%04x\n", data->start_handle, data->end_handle);
}

static void print_uuid(const btmg_uuid_t *uuid) {
    char uuid_str[37];
    btmg_uuid_t uuid128;

    bt_manager_uuid_to_uuid128(uuid, &uuid128);
    bt_manager_uuid_to_string(&uuid128, uuid_str, sizeof(uuid_str));

    printf("%s\n", uuid_str);
}

static void bt_test_gattc_dis_service_cb(gattc_dis_service_cb_para_t *data) {
    if (discovery_flag != 1) {
        bt_manager_gatt_client_discover_service_all_char(data->conn_id, data->attr);
        return;
    }
    ms_hal_ble_stack_msg_t msg;
    bzero(&msg, sizeof(msg));
    msg.event_type = MS_HAL_BLE_STACK_EVENT_DISC_SVC_REPORT;
    msg.param.conn_hdl = data->conn_id;
    msg.param.disc_svc_msg.start_hdl = data->start_handle;
    msg.param.disc_svc_msg.end_hdl = data->end_handle;
    printf("------11-----%d  %d ------------\n", __LINE__, data->uuid.type);
    switch (data->uuid.type) {
    case BT_UUID_16:
        printf("-----------%d  %d  0x%x------------\n", __LINE__, data->uuid.type, data->uuid.value.u16);
        msg.param.disc_svc_msg.uuid_len = 2;
        unsigned char uuid_value[2] = {0};
        uuid_value[0] = data->uuid.value.u16 & 0xff;
        uuid_value[1] = (data->uuid.value.u16 >> 8) & 0xff;
        msg.param.disc_svc_msg.uuid = uuid_value;
        break;
    case BT_UUID_32:
        printf("-----------%d  %d  0x%x------------\n", __LINE__, data->uuid.type, data->uuid.value.u32);
        msg.param.disc_svc_msg.uuid_len = 4;
        unsigned char uuid_value2[4] = {0};
        uuid_value2[0] = data->uuid.value.u32 & 0xff;
        uuid_value2[1] = (data->uuid.value.u32 >> 8) & 0xff;
        uuid_value2[2] = (data->uuid.value.u32 >> 16) & 0xff;
        uuid_value2[3] = (data->uuid.value.u32 >> 24) & 0xff;
        msg.param.disc_svc_msg.uuid = uuid_value2;
        break;
    case BT_UUID_128:
        msg.param.disc_svc_msg.uuid_len = 16;
        msg.param.disc_svc_msg.uuid = data->uuid.value.u128;
        printf("\n\n");
        for (int i = 0; i < 16; i++) {
            printf("0x%x ", data->uuid.value.u128[i]);
        }
        printf("\n\n");
        break;
    default:
        msg.param.disc_svc_msg.uuid_len = 16;
        msg.param.disc_svc_msg.uuid = data->uuid.value.u128;
        break;
    }
    if (ms_hal_ble_stack_callback)
        ms_hal_ble_stack_callback(msg);
    bt_manager_gatt_client_discover_service_all_char(data->conn_id, data->attr);
}

static void bt_test_gattc_dis_char_cb(gattc_dis_char_cb_para_t *data) {
    if (discovery_flag != 2) {
        bt_manager_gatt_client_discover_char_all_descriptor(data->conn_id, data->attr);
        return;
    }
    ms_hal_ble_stack_msg_t msg;
    bzero(&msg, sizeof(msg));
    msg.event_type = MS_HAL_BLE_STACK_EVENT_DISC_CHAR_REPORT;
    msg.param.conn_hdl = data->conn_id;
    msg.param.disc_char_msg.att_hdl = data->start_handle;
    msg.param.disc_char_msg.pointer_hdl = data->value_handle;
    switch (data->uuid.type) {
    case BT_UUID_16:
        msg.param.disc_char_msg.uuid_len = 2;
        printf("-----------%d  %d  0x%x------------\n", __LINE__, data->uuid.type, data->uuid.value.u16);
        unsigned char uuid_value[2] = {0};
        uuid_value[0] = data->uuid.value.u16 & 0xff;
        uuid_value[1] = (data->uuid.value.u16 >> 8) & 0xff;
        msg.param.disc_char_msg.uuid = uuid_value;
        break;
    case BT_UUID_32:
        msg.param.disc_char_msg.uuid_len = 4;
        printf("-----------%d  %d  0x%x------------\n", __LINE__, data->uuid.type, data->uuid.value.u32);
        unsigned char uuid_value2[4] = {0};
        uuid_value2[0] = data->uuid.value.u32 & 0xff;
        uuid_value2[1] = (data->uuid.value.u32 >> 8) & 0xff;
        uuid_value2[2] = (data->uuid.value.u32 >> 16) & 0xff;
        uuid_value2[3] = (data->uuid.value.u32 >> 24) & 0xff;
        msg.param.disc_char_msg.uuid = uuid_value2;
        break;
    case BT_UUID_128:
        msg.param.disc_char_msg.uuid_len = 16;
        msg.param.disc_char_msg.uuid = data->uuid.value.u128;
        printf("\n\n");
        for (int i = 0; i < 16; i++) {
            printf("0x%x ", data->uuid.value.u128[i]);
        }
        printf("\n\n");
        break;
    default:
        msg.param.disc_char_msg.uuid_len = 16;
        msg.param.disc_char_msg.uuid = data->uuid.value.u128;
        break;
    }
    msg.param.disc_char_msg.prop = data->properties;
    if (ms_hal_ble_stack_callback)
        ms_hal_ble_stack_callback(msg);
    bt_manager_gatt_client_discover_char_all_descriptor(data->conn_id, data->attr);
}

static void bt_test_gattc_dis_desc_cb(gattc_dis_desc_cb_para_t *data) {
    if (discovery_flag != 3) {
        return;
    }
    ms_hal_ble_stack_msg_t msg;
    bzero(&msg, sizeof(msg));
    msg.event_type = MS_HAL_BLE_STACK_EVENT_DISC_DESC_REPORT;
    msg.param.conn_hdl = data->conn_id;
    msg.param.disc_desc_msg.att_hdl = data->handle;
    switch (data->uuid.type) {
    case BT_UUID_16:
        msg.param.disc_desc_msg.uuid_len = 2;
        printf("-----------%d  %d  0x%x------------\n", __LINE__, data->uuid.type, data->uuid.value.u16);
        unsigned char uuid_value[2] = {0};
        uuid_value[0] = data->uuid.value.u16 & 0xff;
        uuid_value[1] = (data->uuid.value.u16 >> 8) & 0xff;
        printf("-----------%d  %d  0x%x  0x%x------------\n", __LINE__, data->uuid.type, uuid_value[0], uuid_value[1]);
        msg.param.disc_desc_msg.uuid = uuid_value;
        break;
    case BT_UUID_32:
        msg.param.disc_desc_msg.uuid_len = 4;
        printf("-----------%d  %d  0x%x------------\n", __LINE__, data->uuid.type, data->uuid.value.u32);
        unsigned char uuid_value2[4] = {0};
        uuid_value2[0] = data->uuid.value.u32 & 0xff;
        uuid_value2[1] = (data->uuid.value.u32 >> 8) & 0xff;
        uuid_value2[2] = (data->uuid.value.u32 >> 16) & 0xff;
        uuid_value2[3] = (data->uuid.value.u32 >> 24) & 0xff;
        msg.param.disc_desc_msg.uuid = uuid_value2;
        break;
    case BT_UUID_128:
        msg.param.disc_desc_msg.uuid_len = 16;
        msg.param.disc_desc_msg.uuid = data->uuid.value.u128;
        printf("\n\n");
        for (int i = 0; i < 16; i++) {
            printf("0x%x ", data->uuid.value.u128[i]);
        }
        printf("\n\n");
        break;
    default:
        msg.param.disc_desc_msg.uuid_len = 16;
        msg.param.disc_desc_msg.uuid = data->uuid.value.u128;
        break;
    }
    if (ms_hal_ble_stack_callback)
        ms_hal_ble_stack_callback(msg);
}

static void bt_test_gattc_get_conn_list_cb(gattc_connected_list_cb_para_t *data) {
    if (strcmp((const char *)(data->addr), gattc_select_addr) == 0) {
        gattc_cn_handle = data->conn_id;
        printf("----------------[select]----------------\n");
    }
    printf("[device]: %d %s (%s)\n", data->conn_id, data->addr,
           data->addr_type == BTMG_LE_PUBLIC_ADDRESS ? "public" : (data->addr_type == BTMG_LE_RANDOM_ADDRESS ? "random" : "error"));

    return;
}

void bt_gatt_client_register_callback(btmg_callback_t *cb) {
    cb->btmg_gatt_client_cb.gattc_conn_cb = bt_test_gattc_conn_cb;
    cb->btmg_gatt_client_cb.gattc_disconn_cb = bt_test_gattc_disconn_cb;
    cb->btmg_gatt_client_cb.gattc_read_cb = bt_test_gattc_read_cb;
    cb->btmg_gatt_client_cb.gattc_write_cb = bt_test_gattc_write_cb;
    cb->btmg_gatt_client_cb.gattc_write_long_cb = bt_test_gattc_write_long_cb;
    cb->btmg_gatt_client_cb.gattc_service_changed_cb = bt_test_gattc_service_changed_cb;
    cb->btmg_gatt_client_cb.gattc_notify_indicate_cb = bt_test_gattc_notify_indicate_cb;
    cb->btmg_gatt_client_cb.gattc_dis_service_cb = bt_test_gattc_dis_service_cb;
    cb->btmg_gatt_client_cb.gattc_dis_char_cb = bt_test_gattc_dis_char_cb;
    cb->btmg_gatt_client_cb.gattc_dis_desc_cb = bt_test_gattc_dis_desc_cb;
    cb->btmg_gatt_client_cb.gattc_connected_list_cb = bt_test_gattc_get_conn_list_cb;

    cb->btmg_gap_cb.gap_le_scan_report_cb = bt_test_gattc_scan_report_cb;
}

int bt_gatt_client_init() {
    bt_manager_gatt_client_init();
    return 0;
}

int bt_gatt_client_deinit() {
    bt_manager_gatt_client_deinit();
    return 0;
}

int ble_discovery_all_service(int flag, uint16_t conn_hdl) {
    discovery_flag = flag;
    int ret = bt_manager_gatt_client_discover_all_services(conn_hdl, 0x0001, 0xffff);
    if (ret) {
        return MS_HAL_RESULT_ERROR;
    }
    ms_hal_ble_stack_msg_t msg;
    bzero(&msg, sizeof(msg));
    msg.event_type = MS_HAL_BLE_STACK_EVENT_CMP_SVC_DISC;
    msg.param.conn_hdl = conn_hdl;
    if (ms_hal_ble_stack_callback)
        ms_hal_ble_stack_callback(msg);
    return MS_HAL_RESULT_SUCCESS;
}

void ble_discovery_all_char(uint16_t conn_hdl, uint16_t start_hdl, uint16_t end_hdl) {
    bt_manager_gatt_client_discover_service_all_char(conn_hdl, NULL);
    ;
}

ms_hal_result_t ble_gatt_write(uint8_t type, uint16_t conn_hdl, uint16_t att_handle, uint16_t len, uint8_t *data) {
    int ret = 0;
    if (type == 0) {
        ret = bt_manager_gatt_client_write_request(conn_hdl, att_handle, data, len);
    } else if (type == 1) {
        ret = bt_manager_gatt_client_write_command(conn_hdl, att_handle, 0, data, len);
    }
    if (ret)
        return MS_HAL_RESULT_ERROR;
    else
        return MS_HAL_RESULT_SUCCESS;
}

ms_hal_result_t ble_scan_start(ms_hal_ble_scan_params_t *params, ms_hal_ble_scan_callback_t function) {
    int ret = 0;
    if (!params || !function)
        return MS_HAL_RESULT_ERROR;
    if (ble_scan_status == 1) {
        ble_scan_status = 0;
        bt_manager_le_scan_stop();
        usleep(1000 * 100);
    }
    if (params) {
        scan_param.scan_type = params->active;
        scan_param.scan_interval = params->scan_intvl;
        scan_param.scan_window = params->scan_window;
        scan_param.filter_duplicate = params->filter_duplicated;
        scan_param.own_addr_type = BTMG_LE_PUBLIC_ADDRESS;
        scan_param.filter_policy = params->filter_policy;
        // bt_manager_le_add_white_list((uint8_t *)args[0], addr_type);
        if (params->p_whitelist && params->p_whitelist->addr_count > 0) {
            for (int i = 0; i < params->p_whitelist->addr_count; i++) {
                char addr_buf[32] = {0};
                sprintf(addr_buf, "%02x:%02x:%02x:%02x:%02x:%02x", params->p_whitelist->p_addrs->addr[0],
                        params->p_whitelist->p_addrs->addr[1], params->p_whitelist->p_addrs->addr[2],
                        params->p_whitelist->p_addrs->addr[3], params->p_whitelist->p_addrs->addr[4],
                        params->p_whitelist->p_addrs->addr[5]);
                bt_manager_le_add_white_list(addr_buf, params->p_whitelist->p_addrs->addr_type);
            }
        }
        ret = bt_manager_le_set_scan_parameters(&scan_param);
        printf("ret = %d\n", ret);
        if (ble_scan_status == 0)
            ret = bt_manager_le_scan_start(&scan_param);
    }
    scan_callback = function;
    ble_scan_status = 1;

    ms_hal_ble_stack_msg_t msg;
    bzero(&msg, sizeof(msg));
    msg.event_type = MS_HAL_BLE_STACK_EVENT_SCAN_ON;
    if (ms_hal_ble_stack_callback)
        ms_hal_ble_stack_callback(msg);
    if (ret != 0) {
        ret = 1;
    }
    return ret;
}

ms_hal_result_t ble_scan_stop(void) {
    if (ble_scan_status == 0) {
        return MS_HAL_RESULT_SUCCESS;
    }
    int ret = bt_manager_le_scan_stop();
    usleep(1000 * 100);
    ble_scan_status = 0;
    ms_hal_ble_stack_msg_t msg;
    bzero(&msg, sizeof(msg));
    msg.event_type = MS_HAL_BLE_STACK_EVENT_SCAN_OFF;
    if (ms_hal_ble_stack_callback)
        ms_hal_ble_stack_callback(msg);
    return ret;
}

uint8_t get_ble_scan_status(void) {
    return ble_scan_status;
    ;
}

ms_hal_result_t ble_conn_start(ms_hal_ble_conn_param_t *param, ms_hal_ble_addr_t *target) {
    btmg_le_conn_param_t conn_params;
    conn_params.min_conn_interval = param->conn_intv_min;
    conn_params.max_conn_interval = param->conn_intv_max;
    conn_params.slave_latency = param->slave_latency;
    conn_params.conn_sup_timeout = param->timeout;
    char addr_buf[32] = {0};
    sprintf(addr_buf, "%02x:%02x:%02x:%02x:%02x:%02x", target->addr[0], target->addr[1], target->addr[2], target->addr[3],
            target->addr[4], target->addr[5]);
    printf("addr_buf %s %d\n", addr_buf, target->addr_type);
    int ret = bt_manager_gatt_client_connect(addr_buf, target->addr_type, 517, BTMG_SECURITY_LOW);
    memcpy(now_connect_slave, target->addr, 6);
    bt_manager_le_update_conn_params(&conn_params);
    return ret;
    ;
}
